package hhs.scannerapp;

import static java.lang.Math.pow;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Point;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

public class ModelActivity extends Activity implements GLSurfaceView.Renderer, ColorPickerDialog.OnColorChangedListener{
	private GLSurfaceView glView;
	float angleX = 0.0f, angleY = 0.0f, speed = 0.5f;
	String TAG = "OpenCVTest";
	public Model model = new Model();
	float sensitivity = 0.25f, zoom = 0.03f;
	float scale;
	float zLoc = -6;
	float prevX=-1, prevY=-1, prevDist = 0;
	float zMax = -1;
	float zMin = -15;
	private Mat modelView;
	private Mat modelView2;
	public Cube cube = new Cube();
	String modeltype, thresholdType;
	static final String RECT = "box";
	static final String CIRC = "cylinder";
	private boolean loadTexture = false;
	static final String TEXTURED = "texture";
	static final String COLORED = "color";
	static final String RANDOM_COLORED = "randomcolor";
	static final String NO_TEXTURE = "notexture";
	String textureType = NO_TEXTURE;
	boolean hasTexturePicts;
	String textureConfig; 
	private boolean menuChange = false;
	private int menuChangeNum = 0;
	public String saveType = null;
	private static final String COLOR_PREFERENCE_KEY = "color";

	private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) {
		@Override
		public void onManagerConnected(int status) {
			switch (status) {
			case LoaderCallbackInterface.SUCCESS:
			{
				create();
			} break;
			default:
			{
				super.onManagerConnected(status);
			} break;
			}
		}
	};

	private void create() {
		Log.e("ModelActivity", "Successfully switched Activities");
		Intent intent = getIntent();
		Bundle b = intent.getExtras();
		Bitmap bmp = BitmapFactory.decodeFile("/storage/emulated/0/AppPictures/temp0.png");
		modelView = new Mat(bmp.getHeight(), bmp.getWidth(), CvType.CV_8UC1);
		Utils.bitmapToMat(bmp, modelView);
		bmp.recycle();
		modeltype = b.getString("modeltype");
		if(modeltype.equalsIgnoreCase("box")){
			Log.e("create", "modeltype = box");
			Bitmap bmp2 = BitmapFactory.decodeFile("/storage/emulated/0/AppPictures/temp1.png");
			modelView2 = new Mat(bmp2.getHeight(), bmp2.getWidth(), CvType.CV_8UC1);
			Utils.bitmapToMat(bmp2, modelView2);
			bmp2.recycle();
		}
		thresholdType = b.getString("thresholdtype");
		textureConfig = b.getString("textureconfig");
		if(textureConfig.equalsIgnoreCase("texture")){
			textureType = TEXTURED;
			menuChangeNum = 1;
		}
		
		else{
			textureType = NO_TEXTURE;
			menuChangeNum = 2;
		}
		loadTexture = true;

		glView.setOnTouchListener(new OnTouchListener(){
			@Override
			public boolean onTouch(View v, MotionEvent event) {								
				if(event.getPointerCount() == 1) {
					if(event.getAction() == MotionEvent.ACTION_MOVE && prevX != -1) {
						float deltaX = sensitivity*(event.getX()-prevX);
						float deltaY = sensitivity*(event.getY()-prevY);
						angleX+=deltaX;
						angleY+=deltaY;
						if(angleY < -90)
							angleY = -90;
						if(angleY > 90)
							angleY = 90;
					}
				} else if(event.getPointerCount() == 2) {
					if(event.getAction() == MotionEvent.ACTION_MOVE && prevDist != 0) {
						float newDist = (float) pow(pow(event.getY(1)-event.getY(0),2)+
								pow(event.getX(1)-event.getX(0),2),0.5);

						zLoc+=zoom*(newDist-prevDist);

						if(zLoc < zMin)
							zLoc = zMin;

						if(zLoc > zMax)
							zLoc = zMax;
					}
					prevDist = (float) pow(pow(event.getY(1)-event.getY(0),2)+
							pow(event.getX(1)-event.getX(0),2),0.5);
				}
				prevX = event.getX();
				prevY = event.getY();

				return true;
			}});
		if(RECT.equals(modeltype)) {
			makeCube();
		} else {
			makeModel();
		}
		
		menuChange = true;
		invalidateOptionsMenu();		
	}
	/*@Override
	 * / * model should already work if you get menu
	 /* system working so you can see. Should change
	 * the colors while running, if it doesn't then
	 * it doesn't work
	 */
	public boolean onOptionsItemSelected(MenuItem item) 
	{
		switch (item.getItemId()) 
		{ 
	        case R.id.choose_color:		
	        	int color = PreferenceManager.getDefaultSharedPreferences(ModelActivity.this).getInt(COLOR_PREFERENCE_KEY,Color.WHITE);
	        	ColorPickerDialog cpd = new ColorPickerDialog(ModelActivity.this, ModelActivity.this,color);
	        	cpd.show();
	        	textureType = COLORED;
	        	if(modeltype.equals(RECT))
	        	 cube.makeColors(Color.BLACK);
	        	else
	        	 model.makeColors(Color.BLACK);
	            break;
	        case R.id.picture:
	        	textureType = TEXTURED;
	   		 	loadTexture = true;
	            break;
	        case R.id.random_colors:
	        	textureType = RANDOM_COLORED;
	        	if(modeltype.equals(RECT)){
		        	 cube.makeColors();
	        		 Log.e("RandomColors", "From Menu Called");
	        	}
	        	else
	        	 model.makeColors();
	            break;
	        case R.id.no_texture:
	        	textureType = NO_TEXTURE;
	            break;
	        case R.id.email:
	        	//calls EmailActivity
	        	Intent emailIntent = new Intent(getBaseContext(), EmailActivity.class);
				startActivityForResult(emailIntent, 0);
	            break;
	        case R.id.model:
	        	//creates instance of FileSelector	
	        	//Sample filters array/
	        	final String[] mFileFilter = {".obj", ".stl" };
	        	new FileSelector(this, FileOperation.SAVE, mSaveFileListener, mFileFilter).show();
	            break;
	        case R.id.texture:
	        	//Sample filters array/
	        	final String[] mFileTexFilter = {".png" };
	        	new FileSelector(this, FileOperation.SAVE, mSaveFileListener, mFileTexFilter).show();
	            break;
	        default:
	            break;
        }
        return true;
	}
	
	OnHandleFileListener mSaveFileListener = new OnHandleFileListener() 
	{
		@Override
		public void handleFile(final String filePath) 
		{
			if(saveType.equalsIgnoreCase(".obj"))
				Parser.toObj(filePath, model);
			else if(saveType.equalsIgnoreCase(".stl"))
				Parser.toSTL(filePath, model);
		}
	};

	@Override
	public boolean onPrepareOptionsMenu (Menu menu) 
	{	
		if(menuChange)
		{
			menu.clear();
			if(menuChangeNum == 1)
			{
				getMenuInflater().inflate(R.menu.model_menu, menu);
			}
			else if(menuChangeNum == 2)
			{
				getMenuInflater().inflate(R.menu.color_menu, menu);
			}
		}

		return true;
	}
	
	private void makeCube(){
		EdgeFinder edges = new EdgeFinder(modelView, modelView2, thresholdType);
		float [] values = edges.getCubeValues();
		scale = Math.max(values[0],values[1]);
		scale = Math.max(scale,values[2]);
		scale = 0.5f/scale;
		cube.instantiate(values[0],values[1],values[2]);
//        Parser.toSTL(Environment.getExternalStorageDirectory().getPath() + File.separator+"Models/model", model);
//        Parser.toSTL(Environment.getExternalStorageDirectory().getPath() + File.separator+"Models/model", model);
	}

	private void makeModel() {
		EdgeFinder edges = new EdgeFinder(modelView, thresholdType);
		List<Point> leftEdges = edges.getLeftPoints();
		List<Point> rightEdges = edges.getRightPoints();

		List<EdgeFrame> eFrames = new ArrayList<EdgeFrame>();
		final float N = 24;
		for(int i = 0; i < N; i++) {
			EdgeFrame add = new EdgeFrame();
			add.leftPoints = leftEdges;
			add.rightPoints = rightEdges;
			add.t = (float)(i/N);
			Log.e("OpenCVTest","LRT: "+add.t+" "+i+"/"+N);
			eFrames.add(add);
		}

		//Figure out scale
		double maxRadius = 0f;
		List<EdgeModel> eModels = Frame2Model.toModel(eFrames);
		for(EdgeModel em : eModels)
		{
			for(EdgeModel.EdgePoint ep : em.edges){
				if(ep.r>maxRadius)
					maxRadius = ep.r;
			}
		}

		scale = (float)(0.5f/maxRadius);

		model.instantiate(eModels);
        Parser.toObj(Environment.getExternalStorageDirectory().getPath() + File.separator+"Models/model", model);
        Parser.toSTL(Environment.getExternalStorageDirectory().getPath() + File.separator+"Models/model", model);
	}

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState); //This is king
		glView = new GLSurfaceView(this);
		glView.setRenderer(this);
		setContentView(glView);
		if (!OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_2, this, mOpenCVCallBack))
		{
			Log.e(TAG, "Cannot connect to OpenCV Manager");
		}	super.onCreate(savedInstanceState);
	}

	@Override
	protected void onPause(){
		super.onPause();
		glView.onPause();
		loadTexture = true;
	}

	@Override
	protected void onResume(){
		super.onResume();
		glView.onResume();
	}

	@Override
	public void onDrawFrame(GL10 gl) {
		if(loadTexture) {
			Bitmap bmp = BitmapFactory.decodeFile("/storage/emulated/0/AppPictures/texture.png");
			loadTexture(gl, bmp);
			loadTexture = false;
			bmp.recycle();
		}
		
		if(modeltype != null) {			
			gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
			gl.glLoadIdentity();
			gl.glTranslatef(0, 0.0f, zLoc);
			gl.glScalef(scale, scale, scale);
			gl.glRotatef(angleY, 1.0f, 0.0f, 0.0f);
			gl.glRotatef(angleX, 0.0f, 1.0f, 0.0f);

			if(modeltype.equals(RECT)) {
				cube.draw(gl, textureType);
			} 
			
			else {
				model.draw(gl, textureType);
			}
		}
	}


	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		if(height==0)
			height=1;
		float aspect = (float)width/height;
		gl.glViewport(0, 0, width, height);
		gl.glMatrixMode(GL10.GL_PROJECTION);
		gl.glLoadIdentity();
		GLU.gluPerspective(gl, 45, aspect, 0.1f, 100.0f);
		gl.glMatrixMode(GL10.GL_MODELVIEW);
		gl.glLoadIdentity();
	}

	private void loadTexture(GL10 gl, Bitmap tex) {
		if(modeltype.equals(RECT)) {
			gl.glGenTextures(1, cube.textures, 0);
			gl.glBindTexture(GL10.GL_TEXTURE_2D, cube.textures[0]);
		} 
		
		else {
			gl.glGenTextures(1, model.textures, 0);
			gl.glBindTexture(GL10.GL_TEXTURE_2D, model.textures[0]);
		}
		
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);
		GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, tex, 0);
	}

	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		gl.glClearDepthf(1.0f);
		gl.glEnable(GL10.GL_DEPTH_TEST);
		gl.glDepthFunc(GL10.GL_LEQUAL);
		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
		gl.glShadeModel(GL10.GL_SMOOTH);
		gl.glDisable(GL10.GL_DITHER);
	}
	
	@Override
	public void colorChanged(int color) 
	{
		 PreferenceManager.getDefaultSharedPreferences(this).edit().putInt(COLOR_PREFERENCE_KEY, color).commit();
	}	
}
